﻿using System;
using System.Collections.Generic;
using System.Linq;
using GLWrap;
using GLWrap.SceneObjects;
using ReAnimation.Domain;
using ReAnimation.Domain.Policies.Interfaces;
using ReAnimation.Graphics;
using ReAnimation.Helpers.Common;
using ReAnimation.Helpers.WinForms;
using ReAnimation.MVP.Models.Interfaces;
using ReAnimation.MVP.ViewInterfaces;
using Sharp3D.Math.Core;
using ReAnimation.Graphics.Handlers;
using ReAnimation.Infrastructure.Services.Interfaces;
using ReAnimation.Helpers.DynamicProxy;

namespace ReAnimation.MVP.Views
{
    public partial class MoleculesDisplayView : BaseUserControlView,IMoleculesDisplayView
    {
        private IApplicationSettings _applicationSettings;
        private readonly IMoleculesDisplayModel _moleculesDisplayModel;
        private readonly IGlSceneManager _glSceneManager;
        private readonly IMainModel _mainModel;
        private readonly IVisualizationPolicy _visualizationPolicy;

        public MoleculesDisplayView(IGlSceneManager glSceneManager,
            IApplicationSettings applicationSettings,
                                    IVisualizationPolicy visualizationPolicy,
                                    IMainModel mainModel,
                                    IMoleculesDisplayModel moleculesDisplayModel)
        {
            _applicationSettings = applicationSettings;
            _visualizationPolicy = visualizationPolicy;
            _mainModel = mainModel;
            _moleculesDisplayModel = moleculesDisplayModel;
            _glSceneManager = glSceneManager;
            InitializeComponent();

            openGlControl.InitializeContexts();

            _glSceneManager.Initialize(openGlControl);

            var light = new Light { Position = new Vector3F(-10, 10, 10) };

            _glSceneManager.BackColor = _applicationSettings.BackgroundColor;
            _glSceneManager.AddSceneObject(light);
            _glSceneManager.Refresh();

            _mainModel.CurrentAnimationStepChanged += new EventHandler(_mainModel_CurrentAnimationStepChanged);
            _mainModel.MoleculeAdded += MainModelOnMoleculeAdded;
            _mainModel.MoleculeRemoved += MainModelOnMoleculeRemoved;
            _mainModel.CurrentMoleculeChanged += MainModelOnCurrentMoleculeChanged;
            _mainModel.CurrentMoleculeStructureChanged += MainModelOnCurrentMoleculeStructureChanged;

            _moleculesDisplayModel.UpdateRadiusAndColor += (s, o) => { SyncMoleculeDrawersWithMolecules(); };

            EventsSource.EditSubscriptionsFor(_applicationSettings)
            .AfterPropertySet(() => _applicationSettings.BackgroundColor)
            .Handler(() =>
                {
                    _glSceneManager.BackColor = _applicationSettings.BackgroundColor;
                    _glSceneManager.Refresh();
                });

            EventsSource.EditSubscriptionsFor(_applicationSettings)
                .AfterPropertySet(() => _applicationSettings.LineWeight)
                .AfterPropertySet(() => _applicationSettings.LinkColor)
                .Handler(() =>
                {
                    SyncMoleculeDrawersWithMolecules();
                });
        }

        void _mainModel_CurrentAnimationStepChanged(object sender, EventArgs e)
        {
            if(_mainModel.CurrentAnimationStep != null)
            {
                _glSceneManager.Camera = _mainModel.CurrentAnimationStep.Camera;
            }
        }

        private void MainModelOnCurrentMoleculeStructureChanged(object sender, EventArgs eventArgs)
        {
            SyncMoleculeDrawersWithMolecules();
        }

        private void MainModelOnCurrentMoleculeChanged(object sender, EventArgs eventArgs)
        {
            SyncMoleculeDrawersWithMolecules();
        }

        private void MainModelOnMoleculeRemoved(object sender, EventArg<Molecule> eventArg)
        {
            SyncMoleculeDrawersWithMolecules();
        }

        private void MainModelOnMoleculeAdded(object sender, EventArg<Molecule> eventArg)
        {
            SyncMoleculeDrawersWithMolecules();
        }

        //TODO: call this method via AOP for all class methods
        private void SyncMoleculeDrawersWithMolecules()
        {
            if (_mainModel.CurrentAnimationStep != null)
            {
                foreach (var molecule in _mainModel.CurrentAnimationStep.Molecules)
                {
                    if (!_moleculesDisplayModel.MoleculeDrawers.ContainsKey(molecule))
                    {
                        var moleculeDrawer = new MoleculeDrawer(_visualizationPolicy);
                        moleculeDrawer.LinkColor = _applicationSettings.LinkColor;
                        moleculeDrawer.Init(_glSceneManager);
                        moleculeDrawer.Molecule = molecule;

                        _moleculesDisplayModel.MoleculeDrawers[molecule] = moleculeDrawer;
                    }
                }

                var moleculesToRemove = from molecule in _moleculesDisplayModel.MoleculeDrawers.Keys
                                        where !_mainModel.CurrentAnimationStep.Molecules.Contains(molecule)
                                        select molecule;

                moleculesToRemove.ToArray().ForEach(m =>
                                                      {
                                                          _moleculesDisplayModel.MoleculeDrawers[m].Dispose();
                                                          _moleculesDisplayModel.MoleculeDrawers.Remove(m);
                                                      });

                _moleculesDisplayModel.MoleculeDrawers.Values.ForEach(drawer =>{
                    drawer.LinkColor = _applicationSettings.LinkColor;
                    drawer.Update(false);
                });
            }
            else
            {
                _moleculesDisplayModel.MoleculeDrawers.Values.ForEach((d) =>
                    {
                        d.Dispose();
                    });
                _moleculesDisplayModel.MoleculeDrawers.Clear();
            }

            _glSceneManager.Refresh();
            _glSceneManager.Refresh();//FIXME: нужно вызвать второй раз, незнаю почему. за диплом не платят - разбираться не буду ;-)
        }

        public IMoleculeDrawer GetMoleculeDrawerForMolecule(Molecule molecule)
        {
            if (_moleculesDisplayModel.MoleculeDrawers.ContainsKey(molecule))
                return _moleculesDisplayModel.MoleculeDrawers[molecule];
            return null;
        }
    }
}
